package de.herzog.bbp.view.javafx;

import java.util.regex.Pattern;

import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.herzog.bbp.view.bean.AbstractBean;

public class ValidatedTextField<T> extends TextField {

	private static final Logger LOG = LoggerFactory.getLogger(ValidatedTextField.class);

	public static final String STLYE_CLASS_INVALID = "invalid";
	public static final String STLYE_CLASS_CHECKERROR = "checkerror";

	private ObjectProperty<Boolean> validProperty;

	protected Validator validator;
	protected EditingTableCell<?, T> tableCell;

	protected Pattern editMaskPattern;
	protected Pattern validationPattern;

	public ValidatedTextField() {
		this(null, null, "");
	}

	public ValidatedTextField(Validator validator) {
		this(validator, null, "");
	}

	public ValidatedTextField(Validator validator, EditingTableCell<?, T> tableCell) {
		this(validator, tableCell, "");
	}

	public ValidatedTextField(Validator validator, EditingTableCell<?, T> tableCell, String text) {
		super(text);
		this.validProperty = new SimpleObjectProperty<Boolean>(true);
		this.validator = validator;
		this.tableCell = tableCell;
		this.editMaskPattern = Pattern.compile(validator.getEditMask());
		this.validationPattern = Pattern.compile(validator.getValidationMask());
		this.focusedProperty().addListener(validationListener);
		this.focusedProperty().addListener(checkerrorListener);
	}

	private ChangeListener<? super Boolean> validationListener = (observable, oldFocus, newFocus) -> {
		if (oldFocus && !newFocus) {
			getStyleClass().remove(STLYE_CLASS_INVALID);
			validProperty.set(isValid());
			if (!validProperty.get()) {
				LOG.warn("TextField content " + getText() + " was invalid!");
				getStyleClass().add(STLYE_CLASS_INVALID);
				if (tableCell != null) {
					// tableCell.cancelEdit();
				}
				Platform.runLater(() -> requestFocus());
			} else {
				if (tableCell != null) {
					tableCell.commitEdit(tableCell.getItemObject());
				}
			}
		}
	};

	private ChangeListener<? super Boolean> checkerrorListener = (observable, oldFocus, newFocus) -> {
		if (oldFocus && !newFocus) {
			getStyleClass().remove(STLYE_CLASS_CHECKERROR);
			@SuppressWarnings("unchecked")
			TableView<AbstractBean> tableView = (TableView<AbstractBean>) tableCell.getTableView();
			int index = tableCell.getTableRow().getIndex();
			AbstractBean rowBean = tableView.getItems().get(index);
			String columnId = tableCell.getTableColumn().getId();
			if (rowBean.hasCheckError(columnId)) {
				getStyleClass().add(STLYE_CLASS_CHECKERROR);
				Platform.runLater(() -> requestFocus());
			}
		}
	};

	private boolean isValid() {
		boolean patternMatches = validationPattern == null || validationPattern.matcher(getText()).matches();
		Boolean functionReturnsOK = validator.getValidationFunction() == null
				|| validator.getValidationFunction().apply(getText());
		return patternMatches && functionReturnsOK;
	}

	public ObjectProperty<Boolean> validProperty() {
		return validProperty;
	}

	@Override
	public void replaceText(int start, int end, String text) {
		if (validator.isConvertUpperCase())
			text = text.toUpperCase();
		String oldText = getText();
		String left = oldText.substring(0, start);
		String right = oldText.substring(end);
		String newText = left + text + right;
		if (editMaskPattern.matcher(newText).matches() && newText.length() <= validator.getMaxLength()) {
			super.replaceText(start, end, text);
		} else {
			// ignore
		}
	}

	@Override
	public void replaceSelection(String replacement) {
		replaceText(getSelection(), replacement);
	}

}
